﻿using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;

namespace MyMesh
{

    public class MeshUtils
    {




        //<summary>
        //Calculates and returns the distance from a ray to a triangle.
        //If the ray does not intersect the triangle it returns -1.
        //</summary>
        public static float RayTriangleIntersection(Ray ray, Vector3 vertex1, Vector3 vertex2, Vector3 vertex3)
        {
           
            //Compute vectors along two edges of the triangle.
            Vector3 edge1 = vertex2 - vertex1;
            Vector3 edge2 = vertex3 - vertex1;
            //Cross product of ray direction and edge2 - first part of determinant.
            Vector3 directioncrossedge2 = Vector3.Cross(ray.direction, edge2);
            //Compute the determinant.
            float determinant = Vector3.Dot(edge1, directioncrossedge2);
            //If the ray is parallel to the triangle plane, there is no collision.
            if (determinant > -1e-6f && determinant < 1e-6f) { return -1; }
            //Calculate the inverse determinant
            float inversedeterminant = 1.0f / determinant;
            //Calculate the U parameter of the intersection point.
            Vector3 distanceVector = ray.origin - vertex1;
            float triangleU = Vector3.Dot(distanceVector, directioncrossedge2) * inversedeterminant;
            //Make sure it is inside the triangle.
            if (triangleU < 0f || triangleU > 1f) { return -1; }
            //Calculate the V parameter of the intersection point.
            Vector3 distancecrossedge1 = Vector3.Cross(distanceVector, edge1);
            float triangleV = Vector3.Dot(ray.direction, distancecrossedge1) * inversedeterminant;
            //Make sure it is inside the triangle.
            if (triangleV < 0f || triangleU + triangleV > 1f) { return -1; }
            //Compute the distance along the ray to the triangle.
            float raydistance = Vector3.Dot(edge2, distancecrossedge1) * inversedeterminant;
            //Check if the triangle is behind the ray origin
            if (raydistance < 0f) { return -1; }
            //Return the distance from the ray origin to its intersection with the triangle
            return raydistance;
        }

    }

    //网格Combine工具类
    public class CombineTools : ScriptableObject
    {
        public static void CombineChildren(GameObject parent)
        {
            // Find all mesh filter submeshes and separate them by their cooresponding materials
            List<Material> materials = new List<Material>();
            ArrayList combineInstanceArrays = new ArrayList();

            MeshFilter[] meshFilters = parent.GetComponentsInChildren<MeshFilter>();

            foreach (MeshFilter meshFilter in meshFilters)
            {
                MeshRenderer meshRenderer = meshFilter.GetComponent<MeshRenderer>();
                if (meshRenderer != null)
                {
                    for (int s = 0; s < meshFilter.sharedMesh.subMeshCount; s++)
                    {
                        int materialArrayIndex = materials.FindIndex(p => p.name == meshRenderer.sharedMaterials[s].name);
                        if (materialArrayIndex == -1)
                        {
                            materials.Add(meshRenderer.sharedMaterials[s]);
                            materialArrayIndex = materials.Count - 1;
                        }
                        combineInstanceArrays.Add(new ArrayList());

                        CombineInstance combineInstance = new CombineInstance();
                        combineInstance.transform = meshRenderer.transform.localToWorldMatrix;
                        combineInstance.subMeshIndex = s;
                        combineInstance.mesh = meshFilter.sharedMesh;
                        (combineInstanceArrays[materialArrayIndex] as ArrayList).Add(combineInstance);
                    }
                }
                //Disable child meshes
                meshFilter.gameObject.SetActive(false);
            }

            // Get / Create mesh filter
            MeshFilter meshFilterCombine = parent.GetComponent<MeshFilter>();
            if (!meshFilterCombine)
                meshFilterCombine = parent.AddComponent<MeshFilter>();

            // Combine by material index into per-material meshes
            // also, Create CombineInstance array for next step
            Mesh[] meshes = new Mesh[materials.Count];
            CombineInstance[] combineInstances = new CombineInstance[materials.Count];

            for (int m = 0; m < materials.Count; m++)
            {
                CombineInstance[] combineInstanceArray = (combineInstanceArrays[m] as ArrayList).ToArray(typeof(CombineInstance)) as CombineInstance[];
                meshes[m] = new Mesh();
                meshes[m].CombineMeshes(combineInstanceArray, true, true);

                combineInstances[m] = new CombineInstance();
                combineInstances[m].mesh = meshes[m];
                combineInstances[m].subMeshIndex = 0;
            }

            // Combine into one
            meshFilterCombine.sharedMesh = new Mesh();
            meshFilterCombine.sharedMesh.CombineMeshes(combineInstances, false, false);

            // Destroy other meshes
            foreach (Mesh mesh in meshes)
            {
                mesh.Clear();
                DestroyImmediate(mesh);
            }

            // Get / Create mesh renderer
            MeshRenderer meshRendererCombine = parent.GetComponent<MeshRenderer>();
            if (!meshRendererCombine)
                meshRendererCombine = parent.AddComponent<MeshRenderer>();

            // Assign materials
            Material[] materialsArray = materials.ToArray() as Material[];
            meshRendererCombine.materials = materialsArray;

            //Enable parent object
            parent.SetActive(true);
        }
    }


    //网格导出工具类
    public class ExportTools
    {
        //<summary>
        //Mesh TK Export Tools - By EJM Software
        //Exports Mesh Objects
        //</summary>
        public static string MeshToObjString(MeshFilter mf)
        {
            Mesh m = mf.sharedMesh;
            Material[] mats = mf.GetComponent<Renderer>().sharedMaterials;

            StringBuilder sb = new StringBuilder();

            sb.Append("g ").Append(mf.name).Append("\n");
            foreach (Vector3 v in m.vertices)
            {
                sb.Append(string.Format("v {0} {1} {2}\n", v.x, v.y, v.z));
            }
            sb.Append("\n");
            foreach (Vector3 v in m.normals)
            {
                sb.Append(string.Format("vn {0} {1} {2}\n", v.x, v.y, v.z));
            }
            sb.Append("\n");
            foreach (Vector3 v in m.uv)
            {
                sb.Append(string.Format("vt {0} {1}\n", v.x, v.y));
            }
            for (int material = 0; material < m.subMeshCount; material++)
            {
                sb.Append("\n");
                sb.Append("usemtl ").Append(mats[material].name).Append("\n");
                sb.Append("usemap ").Append(mats[material].name).Append("\n");

                int[] triangles = m.GetTriangles(material);
                for (int i = 0; i < triangles.Length; i += 3)
                {
                    sb.Append(string.Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n",
                        triangles[i] + 1, triangles[i + 1] + 1, triangles[i + 2] + 1));
                }
            }
            return sb.ToString();
        }

        public static void MeshToFile(MeshFilter mf, string filename)
        {
            if (string.IsNullOrEmpty(filename))
                return;
            using (StreamWriter sw = new StreamWriter(filename))
            {
                sw.Write(MeshToObjString(mf));
            }
        }
    }

}


